
from django.shortcuts import render,redirect
from django.http import HttpResponse
from django.views import View
from django.views.generic import ListView
from store import models as storeModels
from django.apps import apps
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.db.models import F,Q,Max
from django.db.models import Value, CharField
from datetime import timedelta
from store.models import *
# Create your views here.
class LedgerView(ListView):
    """
    HomeView for website views.
    ListView 参数
    model 指定模型类（如 model = Book），自动查询该模型的所有数据
    queryset 自定义查询集（如 queryset = Book.objects.filter(price__gt=50)），优先级高于 model
    ordering 指定默认排序方式（如 ordering = ['-publish_date'] 或 ordering = '-price'）
    paginate_by 分页大小（如 paginate_by = 10 表示每页显示 10 条）
    paginate_orphans 最后一页允许的最小剩余条数（如 paginate_orphans = 3，避免最后一页只有 1-2 条数据）
    allow_empty 是否允许空列表（默认 True，空列表时正常显示；设为 False 会返回 404）
    template_name 自定义模板路径（如 template_name = 'myapp/book_list.html'）
    template_name_suffix 模板名后缀（默认 _list，结合模型名自动生成模板名，如 book_list.html）
    context_object_name 模板中使用的列表变量名（默认是模型名小写加 _list，如 book_list；可自定义为 books）
    get_context_data()	 重写该方法添加额外上下文（如传递分类、统计数据等）
    def get_context_data(self, **kwargs):
        context = super().get_context_data(** kwargs)
        context['category'] = "小说"  # 额外传递分类信息
        context['total_count'] = self.get_queryset().count()  # 总条数
        return context
    page_kwarg 分页参数名（默认 page，如 URL 中的 ?page=2；可改为 p 即 ?p=2）
    paginator_class 自定义分页器类（默认 django.core.paginator.Paginator）
    http_method_names 允许的 HTTP 方法（默认 ['get', 'head', 'options']，通常无需修改）
    extra_context 静态额外上下文（字典形式，如 extra_context = {'title': '书籍列表'}）

    """
    def get_queryset_sub(self,query:str,begin_date,after_date,wl:str,model:object=StoreRawDetailModel):
        # 1. 为两个模型的查询集指定相同的字段（字段名和顺序必须一致）
        raw_query = model.objects.select_related('raw', 'store_order', 'location', 'unit').annotate(
            wl=Value(wl, output_field=CharField())
        ).filter(Q(Q(raw__name__icontains=query) | Q(wl__icontains=query)),
                 store_order__date__range=[begin_date, after_date]).values(
            'id',
            'store_order__orderId',
            'raw__name',
            'raw__gui_ge',
            'batch_number',
            'location__name',
            'unit__unit',
            'store_num',
            'price',
            'tax',
            'remaining_num',
            'wl',
            'store_order__date'
        )
        return raw_query
    def get_queryset_sub_by_batch_number(self,query:str,begin_date,after_date,wl:str,model:object=StoreRawDetailModel):
        # 1. 为两个模型的查询集指定相同的字段（字段名和顺序必须一致）
        raw_query = model.objects.select_related('raw', 'store_order', 'location', 'unit').annotate(
            wl=Value(wl, output_field=CharField()),
            dw=Value('/', output_field=CharField()),
        ).filter(Q(Q(raw__name__icontains=query) | Q(wl__icontains=query)),
                 store_order__date__range=[begin_date, after_date]).values(

            'raw__name',
            'batch_number',

        ).annotate(
            # 对其他字段进行聚合处理（根据需求选择合适的聚合函数）
             # 如果 id 唯一，可直接用 F()；否则可能需要聚合
            id=Max('id'),
            store_order__orderId=Max('store_order__orderId'),

            raw__gui_ge=Max('raw__gui_ge'),

            location__name=Max('location__name'),
            unit__unit=Max('dw'),
            store_num=Sum(F('store_num')*F('unit__unit_rel')),  # 示例：求和
            price=Max('price'),
            tax=Max('tax'),
            remaining_num=Sum(F('remaining_num') * F('unit__unit_rel')),  # 示例：求和
            wl=Max('wl'),
            store_order__date=Max('store_order__date')
        ).values(
            # 最终指定字段顺序：将 raw__name 放在 store_order__orderId 后面

            'id',
            'store_order__orderId',
            'raw__name',  # 调整到此处
            'raw__gui_ge',
            'batch_number',  # 保留分组字段
            'location__name',
            'unit__unit',
            'store_num',
            'price',
            'tax',
            'remaining_num',
            'wl',
            'store_order__date'
        )
        return raw_query
    model=storeModels.StoreRawDetailModel
    template_name='search/index.html'
    context_object_name='storedetails'
    paginate_by = 30  # 分页，每页显示10条
    extra_context={'today':timezone.now().date()}
    def get_queryset(self):
        today = timezone.now().date()
        # 当月最后一天：下个月第一天减1天
        first_day_of_next_month = (today.replace(day=1) + timedelta(days=32)).replace(day=1)
        last_day_of_month = first_day_of_next_month - timedelta(days=1)
        select_type=self.request.GET.get('select_type','0')
        begin_date=self.request.GET.get('begin_date', str(today.replace(day=1)))
        after_date = self.request.GET.get('after_date',str(last_day_of_month))
        query = self.request.GET.get('q', '')
        if select_type=='0': #按照货位分类
            raw_query=self.get_queryset_sub(query=query,begin_date=begin_date,after_date=after_date,wl='原料',model=StoreRawDetailModel)
            additive_query = self.get_queryset_sub(query=query,begin_date=begin_date,after_date=after_date,wl='添加剂',model=StoreAdditiveDetailModel)
            packaging_query = self.get_queryset_sub(query=query,begin_date=begin_date,after_date=after_date,wl='包材',model=StorePackagingDetailModel)
            finished_query = self.get_queryset_sub(query=query,begin_date=begin_date,after_date=after_date,wl='成品',model=StoreFinishedDetailModel)
            intermediate_query = self.get_queryset_sub(query=query,begin_date=begin_date,after_date=after_date,wl='中间品',model=StoreIntermediateDetailModel)
        else:
            raw_query = self.get_queryset_sub_by_batch_number(query=query, begin_date=begin_date, after_date=after_date, wl='原料',
                                              model=StoreRawDetailModel)
            additive_query = self.get_queryset_sub_by_batch_number(query=query, begin_date=begin_date, after_date=after_date,
                                                   wl='添加剂', model=StoreAdditiveDetailModel)
            packaging_query = self.get_queryset_sub_by_batch_number(query=query, begin_date=begin_date, after_date=after_date,
                                                    wl='包材', model=StorePackagingDetailModel)
            finished_query = self.get_queryset_sub_by_batch_number(query=query, begin_date=begin_date, after_date=after_date, wl='成品',
                                                   model=StoreFinishedDetailModel)
            intermediate_query = self.get_queryset_sub_by_batch_number(query=query, begin_date=begin_date, after_date=after_date,
                                                       wl='中间品', model=StoreIntermediateDetailModel)



        # 2. 执行UNION ALL（all=True表示不去重）
        return raw_query.union(additive_query, all=True
                               ).union(packaging_query,all=True
                            ).union(finished_query,all=True).union(intermediate_query,all=True)
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        # 获取模型的所有字段（排除id）
        model_fields = []
        # 通过模型类获取字段元数据（避免使用模板中的_storerawdetails.0._meta_）
        # for field in self.model._meta.fields:
        #     if field.name == 'id':  # 排除id字段
        #         model_fields.append({
        #             'name': field.name,
        #             'verbose_name': field.verbose_name  # 字段的友好名称
        #         })
        model_fields = [
            {'name': 'id', 'verbose_name': 'id'},
            {'name': 'wl', 'verbose_name': '类别'},
            {'name': 'store_order__date', 'verbose_name': '日期'},
            {'name': 'raw__name', 'verbose_name': '品名'},
            {'name': 'raw__gui_ge', 'verbose_name': '规格'},
            {'name': 'batch_number','verbose_name': '批号'},
            {'name': 'remaining_num', 'verbose_name': '剩余数量'},
            {'name': 'unit__unit', 'verbose_name': '单位'},
            {'name': 'location__name', 'verbose_name': '货位'},
                        ]
        # 将字段信息传递到模板
        context['model_fields'] = model_fields

        # 2. 为每个对象生成“字段值列表”（顺序与model_fields一致）
        item_values_list = []
        for item in context['storedetails']:
            values = []
            for field in model_fields:
                field_name = field['name']
                # 获取字段值并格式化
                if field_name == 'publish_date':
                    # 日期字段格式化
                    value = item.publish_date.strftime("%Y-%m-%d") if item.publish_date else "-"
                elif field_name == 'price':
                    # 价格字段格式化（保留2位小数）
                    value = f"{item.price:.2f}" if item.price else "-"
                else:

                    if isinstance(item, dict):
                        # 如果是字典，直接用 get 方法取值
                        value = item.get(field_name, "-")
                    else:
                        # 如果是类实例，用 getattr 取值
                        value = getattr(item, field_name, "-")

                values.append(value)
            item_values_list.append(values)
        context['item_values_list'] = item_values_list  # 传递值列表

        return context




class Home2View(LoginRequiredMixin,View):
    """
    明细账资料

    """
    SEARCH_TYPES={
        'subledger':'明细账'
    }
    FIELDS = {
        'date': '日期',
        'type': '事项',
        'num': '数量',
        'unit': '单位',
        'unit_rel': '单位关系',
        'remaining_num': '剩余数量（kg）',
        'remark':'备注',
        'remaning_no':'剩余'
    }
    # 1. LoginRequiredMixin 配置：未登录时跳转的登录页URL
    login_url = '/admin/login/'  # 可改为你的自定义登录页（如 '/login/'）
    # 未登录时，跳转后保留原请求地址（便于登录后返回），默认是 'next'
    redirect_field_name = 'next'
    # 2. PermissionRequiredMixin 配置：需要验证的权限（格式：应用名.权限代码）
    # 示例：要求用户拥有 'store' 应用下的 'can_view_subledger' 权限（需先在模型中定义该权限）
    # permission_required = 'store.can_view_subledger'
    # 若需要多个权限，用列表：permission_required = ['store.perm1', 'store.perm2']
    # permission_required = ['store.view_subledger_storerawdetailmodel',
    #                        'store.view_subledger_storeadditivedetailmodel',
    #                        'store.view_subledger_storepackagingdetailmodel',
    #                        'store.view_subledger_storeintermediatedetailmodel',
    #                        'store.view_subledger_storefinisheddetailmodel',]
    # 可选：权限不足时返回 403 页面（默认会跳转 to login_url，建议开启）
    # raise_exception = True

    # #3. 重写 has_permission 方法，实现“或”权限判断
    # def has_permission(self):
    #     # 获取当前登录用户
    #     user = self.request.user
    #
    #     # 若用户是超级管理员，直接拥有所有权限（可选，根据需求决定是否保留）
    #     if user.is_superuser:
    #         return True
    #
    #     #核心逻辑：判断用户是否拥有 permission_required 中的任意一个权限
    #    # 遍历权限列表，只要有一个权限满足，就返回 True
    #     return any(
    #         user.has_perm(perm)  # 检查单个权限
    #         for perm in self.permission_required  # 循环所有配置的权限
    #     )
    # ----------------------------------------------------------------------
    def get_model(self,app_label,model_name):
        current_Model = None
        for model in apps.get_models():
            # 构建选项值，格式为 "app_label.model_name"
            value = f"{model._meta.app_label}.{model.__name__}"
            # 显示文本，使用模型的 verbose_name
            # label = f"{model._meta.app_label} - {model._meta.verbose_name}"
            if f"{app_label}.{model_name}" == value.lower():
                current_Model = model
                break
        return current_Model
    def get_datas_sub_ledger(self,pk,model,select_type):
        list_reuslt = []
        qsets=[]
        # 获取入库数据
        #qset=storeModels.StoreRawDetailModel.objects.filter(pk=pk).first()
        if select_type=='0': #根据货位
            qset = model.objects.filter(pk=pk).first()
            qsets.append(qset)
        elif select_type=='1':#根据品名批号
            # 1. 先查询 pk=1 的记录对应的品名（假设品名字段为 raw__name 或 raw.name）
            # 注意：这里的 pk=1 可能需要替换为你实际的关联主键，比如传入的 pk 参数
            target_raw = model.objects.filter(pk=pk).values('raw__name','batch_number').first()
            if target_raw:
                # 2. 再查询所有品名为该值的记录（pinming IN (...)）
                qsets = model.objects.filter(raw__name=target_raw['raw__name'],batch_number=target_raw['batch_number'])
            else:
                # 如果 pk=pk 的记录不存在，返回空查询集
                qsets = model.objects.none()
        else:
            qsets = model.objects.none()
        for qset in qsets:

            temp = {
                'date': qset.create_at,
                'type': '入库',
                'num': qset.store_num,
                'unit': qset.unit.unit,
                'unit_rel': qset.unit.unit_rel,
                'remark':qset.store_order.remark

            }
            if qset.parent:
                temp['type']='调拨入库'

            list_reuslt.append(temp)
            # 获取报损数据
            loss_details = qset.loss_details.all()
            for loss in loss_details:
                temp = {
                    'date': loss.create_at,
                    'type': '报损',
                    'num': -loss.loss_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark':loss.loss_order.remark
                }
                list_reuslt.append(temp)
            # 获取报溢数据
            surplus_details = qset.surplus_details.all()
            for surplus in surplus_details:
                temp = {
                    'date': surplus.create_at,
                    'type': '报溢',
                    'num': surplus.surplus_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }
                list_reuslt.append(temp)
            # 获取出库数据
            outbound_details = qset.outbound_details.all()
            for outbound in outbound_details:
                temp = {
                    'date': outbound.create_at,
                    'type': f'车间出库:{outbound.out_order.product.finished.name}',
                    'num': -outbound.outbound_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }

                list_reuslt.append(temp)
            # 获取其他出库数据
            otheroutbound_details = qset.otheroutbound_details.all()
            for outbound in otheroutbound_details:
                temp = {
                    'date': outbound.create_at,
                    'type': f'其他出库',
                    'num': -outbound.outbound_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }

                list_reuslt.append(temp)
            # 获取出库退回数据
            back_details = qset.back_details.all()
            for back in back_details:
                temp = {
                    'date': back.create_at,
                    'type': f'车间出库:{back.back_order.product.finished.name}',
                    'num': back.back_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }
                list_reuslt.append(temp)
            # 获取半成品出库数据
            outbound_details = qset.outbound_details_intermediate_product.all()
            for outbound in outbound_details:
                temp = {
                    'date': outbound.create_at,
                    'type': f'车间出库:{outbound.out_order.product.finished.name}',
                    'num': -outbound.outbound_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }

                list_reuslt.append(temp)
            # 获取出库退回数据
            back_details = qset.back_details_intermediate_product.all()
            for back in back_details:
                temp = {
                    'date': back.create_at,
                    'type': f'车间入库:{back.back_order.product.finished.name}',
                    'num': back.back_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }
                list_reuslt.append(temp)
            # 获取销售数据
            sell_details = qset.sell_details.all()
            for sell in sell_details:
                temp = {
                    'date': sell.create_at,
                    'type': f'销售:{sell.sell_order.customer.name}',
                    'num': -sell.sell_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }
                list_reuslt.append(temp)
            # 获取销售退回数据
            sellback_details = qset.sellback_details.all()
            for sellback in sellback_details:
                temp = {
                    'date': sellback.create_at,
                    'type': f'销售:{sellback.sellback_order.customer.name}',
                    'num': sellback.sellback_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }

                list_reuslt.append(temp)
            # 获取调拨数据
            transfer_details = qset.store_details_before.all()
            for transfer in transfer_details:
                temp = {
                    'date': transfer.create_at,
                    'type': f'调拨至:{transfer.to_location.name}',
                    'num': -transfer.transfer_num,
                    'unit': qset.unit.unit,
                    'unit_rel': qset.unit.unit_rel,
                    'remark': ''
                }

                list_reuslt.append(temp)
            # 获取入库退回数据
            try:
                storeback_details = qset.store_back_details.all()
                for storeback in storeback_details:
                    temp = {
                        'date': storeback.create_at,
                        'type': f'退回供应商:{storeback.storeback_order.supplier.name}',
                        'num': -storeback.storeback_num,
                        'unit': qset.unit.unit,
                        'unit_rel': qset.unit.unit_rel,
                        'remark': ''
                    }
                    list_reuslt.append(temp)
            except:
                pass

        list_reuslt.sort(key=lambda x: x['date'])
        for index, item in enumerate(list_reuslt, 0):
            if index == 0:
                item['remaining_num'] = item.get('num', 0) * item.get('unit_rel', 0)
                item['remaning_no']= item.get('num', 0)
                continue
            item['remaining_num'] = list_reuslt[index - 1].get('remaining_num') + item.get('num', 0) * item.get('unit_rel',0)
            item['remaning_no'] = list_reuslt[index - 1].get('remaning_no') + item.get('num', 0)
        for index, item in enumerate(list_reuslt, 0):
            item['remaning_no'] = f"{item['remaning_no']}{item.get('unit', '')}"
        return qsets[0],list_reuslt
    def get_required_perm(self,app_label, model_name, search_type):
        if search_type == 'subledger':
            # 'store.view_subledger_storerawdetailmodel'
            return f"{app_label}.view_{search_type}_{model_name.lower()}"
        else:
            return ''
    def get(self,request,app_label,model_name,search_type):
        current_Model=self.get_model(app_label,model_name)
        context = {'app_label': app_label, 'model_name': current_Model._meta.verbose_name, 'search_type': self.SEARCH_TYPES.get(search_type)}
        context['FIELDS']=self.FIELDS
        pk = request.GET.get('pk')
        select_type=request.GET.get('select_type')  if request.GET.get('select_type') else '0'

        # 步骤2：根据参数动态获取需要验证的权限
        required_perm = self.get_required_perm(app_label, model_name, search_type)

        # 步骤3：权限验证（超级管理员直接通过，普通用户需验证权限）
        user = request.user
        # 超级管理员豁免权限验证（可选，根据需求决定是否保留）
        if not (user.is_superuser or user.has_perm(required_perm)):
            # 无权限：返回 403 响应（可渲染自定义 403 页面）
            return render(request, 'search/403.html', status=403)
        if search_type == 'subledger': #明细分类账
            qset,list_reuslt=self.get_datas_sub_ledger(pk,current_Model,select_type)
            context['qset'] = qset
            context['list_reuslt'] = list_reuslt

        return render(request,'search/details.html',context)
